package com.fuint.common.aspect;

import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 控制器日志
 *
 * Created by FSQ
 * CopyRight https://www.fuint.cn
 */
@Component  // 声明组件
@Aspect // 声明切面
@ComponentScan  //组件自动扫描
@EnableAspectJAutoProxy // spring自动切换JDK动态代理和CGLIB
public class LogAop {

    /**
     *自定义日志
     */
    private Logger logger = LoggerFactory.getLogger(LogAop.class);

    /**
     * 打印类method的名称以及参数
     * @param point 切面
     */
    public void printMethodParams(JoinPoint point){
        if (point == null) {
            return;
        }
        try {
            // 获取方法的参数值数组。方法名、类型以及地址等信息
            String className = point.getTarget().getClass().getName();
            String methodName = point.getSignature().getName();

            // 重新定义日志
            logger = LoggerFactory.getLogger(point.getTarget().getClass());
            logger.info("-------------------------"+className+"------------------------------------");
            logger.info("methodName = {}", methodName);

            // 获取方法的参数值数组
            Object[] methodArgs = point.getArgs();

            // 获取方法参数名称
            String[] paramNames = getFieldsName(className, methodName);

            // 输出方法的参数名和参数值
            printParams(paramNames, methodArgs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用获取方法参数名称
     * @param class_name    类名
     * @param method_name   方法名
     * @throws Exception
     */
    private String[] getFieldsName(String class_name, String method_name) throws Exception {
        Class<?> clazz = Class.forName(class_name);
        String clazz_name = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);
        try {
            CtClass ctClass = pool.get(clazz_name);
            CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
            MethodInfo methodInfo = ctMethod.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr == null) {
                return null;
            }
            String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
            int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
            for (int i = 0; i < paramsArgsName.length; i++) {
                paramsArgsName[i] = attr.variableName(i + pos);
            }
            return paramsArgsName;
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 判断是否为基本类型
     */
    private boolean isPrimite(Class<?> clazz) {
        if (clazz.isPrimitive() || clazz == String.class){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 打印方法参数值  基本类型直接打印，非基本类型需要重写toString方法
     * @param paramsArgsName    方法参数名数组
     * @param paramsArgsValue   方法参数值数组
     */
    private void printParams(String[] paramsArgsName, Object[] paramsArgsValue) {
        if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) {
            return;
        }
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < paramsArgsName.length; i++) {
            // 参数名
            String name = paramsArgsName[i];
            // 参数值
            Object value = paramsArgsValue[i];
            buffer.append(name +" = ");
            if (isPrimite(value.getClass())) {
                buffer.append(value + " ,");
            } else {
                buffer.append(value.toString() + " ,");
            }
        }
        logger.info("params : " + buffer.toString());
        logger.info("-------------------------------------------------------------");
    }

    /**
     * 在方法执行前进行切面
     * 定义切面表达式
     * @param point 切面
     */
    //@Before("execution(public * com.fuint.module..*.*(..))")
    @Around("@annotation(io.swagger.annotations.ApiOperation)")
    public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
        //this.printMethodParams(point);
        Object result = null;
        long beginTime = System.currentTimeMillis();


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> aClass = joinPoint.getTarget().getClass();
        Api annotation = aClass.getAnnotation(Api.class);
        String moduleName = annotation.value();
        if (moduleName != null) {
            logger.debug("模块：" + moduleName);
        }
        Method method = signature.getMethod();
        ApiOperation logAnnotation = method.getAnnotation(ApiOperation.class);
        String operateValue = "";
        if (logAnnotation != null) {
            // 注解上的描述
            operateValue = logAnnotation.value();
            logger.debug("操作：" + logAnnotation.value());
        }
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        logger.debug("方法：" + className + "." + methodName + "()");
        // 请求的方法参数值
        Object[] args = joinPoint.getArgs();
        // 请求的方法参数名称
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        // 拼接请求参数
        if (args != null && paramNames != null) {
            String params = "";

            for (int i = 0; i < args.length; i++) {
                if(this.isIngoreParam(args[i])){//不处理httprequest，multiFile等对象
                    continue;
                }
                params += "  " + paramNames[i] + ": " + args[i];
            }
            logger.debug("入参：" + params);
        }
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 从获取RequestAttributes中获取HttpServletResponse的信息
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                logger.debug(cookie.getName() + " " + cookie.getValue());
            }
        }
        logger.debug("请求者的IP：" + request.getRemoteHost());

        try {
            // 执行方法
            result = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        // 保存操作日志
        saveLog(joinPoint, time);
        int status = response.getStatus();
        logger.debug("http状态码 " + status);
        logger.debug("执行时间：" + time + " 毫秒");
        logger.debug("{},返回：{}",operateValue,JSON.toJSONString(result));
        //logger.debug(JSON.toJSONString(Object));
        return result;
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        //MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //Class<?> aClass = joinPoint.getTarget().getClass();
        //Api annotation = aClass.getAnnotation(Api.class);
        //String moduleName = annotation.value();
        //if (moduleName != null) {
        //    logger.debug("模块：" + moduleName);
        //}
        //Method method = signature.getMethod();
        //ApiOperation logAnnotation = method.getAnnotation(ApiOperation.class);
        //if (logAnnotation != null) {
        //    // 注解上的描述
        //    logger.debug("操作：" + logAnnotation.value());
        //}
        //// 请求的方法名
        //String className = joinPoint.getTarget().getClass().getName();
        //String methodName = signature.getName();
        //logger.debug("方法：" + className + "." + methodName + "()");
        //// 请求的方法参数值
        //Object[] args = joinPoint.getArgs();
        //// 请求的方法参数名称
        //LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        //String[] paramNames = u.getParameterNames(method);
        //// 拼接请求参数
        //if (args != null && paramNames != null) {
        //    String params = "";
        //    for (int i = 0; i < args.length; i++) {
        //        params += "  " + paramNames[i] + ": " + args[i];
        //    }
        //    logger.debug("参数：" + params);
        //}
        //// 获取RequestAttributes
        //RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //// 从获取RequestAttributes中获取HttpServletRequest的信息
        //HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //// 从获取RequestAttributes中获取HttpServletResponse的信息
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        //Cookie[] cookies = request.getCookies();
        //if (cookies != null) {
        //    for (Cookie cookie : cookies) {
        //        logger.debug(cookie.getName() + " " + cookie.getValue());
        //    }
        //}
        //logger.debug("请求者的IP：" + request.getRemoteHost());

    }

    private boolean isIngoreParam(Object param){
        return param != null
                && (param instanceof HttpServletRequest || param instanceof HttpServletResponse
                || param instanceof MultipartFile || param instanceof MultipartFile[])
                ;
    }
}
